<template>
  <div ref="previewEl" class="preview">
    <!-- banner -->
    <studio-header />
    <div class="content-box" :class="{ 'report-mode': isReportMode }">
      <div id="print" class="container" :style="containerStyle">
        <widgets-layout :layout="rootLayout" />
      </div>
      <!-- 报告模式下的下载button -->
      <div
        v-if="isReportMode"
        class="download-btn"
        title="另存为PDF"
        @click="printReport"
      >
        <a-icon-font type="iconxiazaibendi" class="btn-icon"></a-icon-font>
      </div>
    </div>
  </div>
</template>

<script lang="ts">
import { jsPDF as JsPdf } from 'jspdf'
import { Component, Vue, Watch } from 'vue-property-decorator'
import StudioHeader from '@/components/studio/StudioHeader.vue'
import VisualizationStore from '@/store/modules/visualization'
import VisualizationPreviewStore from '@/store/modules/visualization-preview'
import {
  componentTypeEnum,
  ControlConfigurationInterface,
  GridItemDataInterface,
  gridItemFilterInterface,
  KeyValueStore,
} from '@/components/vueGridLayout/interfate/grid-Item-data-inteface'
import { cloneDeep, isArray, isString } from 'lodash'
import { filterFormTypeName, GEOMAP_NAME, GRAPH_ARRAY } from '@/config/contant'
import { getWidgetData } from '@/api/widget'
import {
  bindChartData,
  checkFilterFormIsEnabled,
  checkIsFilterForm,
  filterGraphData,
  getFilterDataFromFilterForm,
  mergeFilterFormFilters,
  handleInteractionActionObject,
} from '@/util/common'
import { checkArrayIncludes } from '@/util/util'
import UserStore from '@/store/modules/user'
import WidgetsLayout from '@/components/visualization/widgets-layout.vue'

import {
  getWidgetGraphData,
  parseGetDataResult,
  parseChartOptionToFunction,
  handleScrollBar,
} from '@/components/common/VisualizationDashboard/util'
import { message } from 'ant-design-vue'
import { HoverFilterFormInfo } from '@/components/visualization/util'
import { publicPath } from '@/api/constants'
import * as htmlToImage from 'html-to-image'

//  preview 中不支持算子控件的联动效果

@Component({
  components: {
    StudioHeader,
    WidgetsLayout,
  },
})
export default class PreviewView extends Vue {
  private publicPath = publicPath
  //  根据主题配置 获取背景设置
  public get containerStyle() {
    return {
      width: `${this.dataStore.layoutData.width}px`,
      height: 'auto',
      background: this.getThemeBgSet(),
    }
  }

  //  根据主题返回背景颜色/图片设置
  private getThemeBgSet() {
    const { theme } = this.dataStore.layoutData.themeSetting
    const { color, type } = this.dataStore.layoutData.themeSetting.panelBg
    if (type === 'pure' && !['blue', 'nebula'].includes(theme)) {
      return color
    }
    switch (theme) {
      case 'light':
      case 'dark':
        return color
      case 'textured':
        return `url(${publicPath}/img/vis-sys/textured-bg.jpg) top/100% repeat-y`
      case 'simple':
        return `url(${publicPath}/img/vis-sys/simple-bg.jpg) top/100% repeat-y`
      case 'nebula':
        return `url(${publicPath}/img/vis-sys/nebula-bg.jpg) top/100% repeat-y`
      case 'blue':
        return `url(${publicPath}/img/vis-sys/blue-bg.jpg) top/100% repeat-y`
      default:
        return '#FFF'
    }
  }

  get projectId() {
    return (this.$route.params as any).id
  }

  get gridList() {
    return VisualizationPreviewStore.gridList
  }

  private get rootLayout() {
    return this.gridList.filter((item) => {
      return (
        item.chartOptions.parentId === undefined ||
        item.chartOptions.parentId === 'root'
      )
    })
  }

  private get currentDashboard() {
    return this.dataStore.currentDashboard
  }

  private get isReportMode() {
    return this.currentDashboard.type === '报告'
  }

  // public chartInstanceMaps: KeyValueStore = {}

  public isFilterFormTaskRunning: boolean = false

  // public barChart = 'barChart'

  private interactRelateList: Array<any> = [] //  [{child:..., parents:...}]  联动列表
  private reverstInteractList: Array<any> = [] //  [{parent:..., children:...}] 反向联动列表
  private selectedParentList: Array<Number> = [] //  联动功能 - 被选择的父item 列表

  filterFormExecuteId: string | number = ''
  /**
   * 报告画布 生成pdf 文件
   */
  private async printReport() {
    const loadingMessage = message.loading('PDF下载中...', 0)
    const element: any = document.querySelector('#print')
    const scale = 2

    // remove scrollbar in gridItem
    handleScrollBar('remove')

    const canvas = await htmlToImage.toJpeg(element, {
      //  improve image quality
      width: element.clientWidth * scale, //  渲染前要应用于节点的高度和宽度（以像素为单位）即图像的大小
      height: element.clientHeight * scale,
      //  属性要在渲染之前复制到节点的样式。 即应用于原元素的样式；【生成的image 大小扩大一倍，那么原元素的大小也要放大一倍】
      style: { transform: `scale(${scale})`, transformOrigin: 'top left' },
      backgroundColor: '#fff',
      filter: (node: any) => node.className !== 'gap',
      quality: 0.5,
    })
    const contentWidth = element.clientWidth
    const contentHeight = element.clientHeight
    //  一页pdf html页面生成的canvas高度 【比例的换算】【根据a4 换算】
    const pageHeight = (contentWidth / 595) * 842
    //  未生成pdf的html页面高度
    let leftHeight = contentHeight
    //  页面偏移
    let position = 0
    //  a4纸的尺寸[595,841.89]，html页面生成的canvas在pdf中图片的宽高
    const imgWidth = 595
    //  【比例的换算】
    const imgHeight = (595 / contentWidth) * contentHeight
    //  设置pdf的尺寸，pdf要使用pt单位 已知 1pt/1px = 0.75 pt = (px/scale)* 0.75
    const pdf = new JsPdf('p', 'pt', 'a4')
    //  记录总页数
    let pageNumber = 0

    //  有两个高度需要区分，一个是html页面的实际高度，和生成pdf的页面高度(841.89)
    //  当内容未超过pdf一页显示的范围，无需分页
    if (leftHeight < pageHeight) {
      pdf.addImage(canvas, 'JPEG', 0, 0, imgWidth, imgHeight)
      //  x, y, width, height; x 相对于canvas页面左边缘的坐标; width 添加的图像的宽度（以 PDF 文档开始时声明的单位
    } else {
      while (leftHeight > 0) {
        pdf.addImage(canvas, 'JPEG', 0, position, imgWidth, imgHeight)
        leftHeight -= pageHeight
        position -= 842 + 20
        pageNumber += 1
        //  避免添加空白页
        if (leftHeight > 0) {
          pdf.addPage()
        }
      }
    }
    //  因为会多生成一页空白页，故将最后一页删除
    pdf.deletePage(pageNumber)

    loadingMessage()
    pdf.save('content.pdf')
    // add back scroll bar
    handleScrollBar('add')
  }
  //  初始化PreviewStore
  private initVisualizationPreviewStore() {
    VisualizationPreviewStore.setSelectedChartInfo([])

    const previewGridList = this.sortLayoutItemsByRowCol(
      JSON.parse(
        JSON.stringify(
          VisualizationStore.gridList.map((gridItem) => {
            return {
              ...gridItem,
              chartOptions: {
                ...(gridItem.chartOptions || {}),
                ...handleInteractionActionObject(gridItem.chartOptions),
              },
            }
          })
        )
      )
    )
    VisualizationPreviewStore.setGridList(previewGridList)
    VisualizationPreviewStore.setLayoutData(
      JSON.parse(JSON.stringify(VisualizationStore.layoutData))
    )
    VisualizationPreviewStore.setPipelineId(VisualizationStore.pipelineId)
    VisualizationPreviewStore.setDataset(
      JSON.parse(JSON.stringify(VisualizationStore.dataset))
    )
    VisualizationPreviewStore.setAllWidgetList(
      cloneDeep(VisualizationStore.widgetList)
    )
    VisualizationPreviewStore.setCurrentDashboardData(
      cloneDeep(VisualizationStore.currentDashboard)
    )
    VisualizationPreviewStore.setCurrentGridItem(
      cloneDeep(VisualizationStore.currentGridItem)
    )
    VisualizationPreviewStore.setActiveSettingItem(
      VisualizationStore.activeSettingItem
    )
    VisualizationPreviewStore.setGridColNum(VisualizationStore.gridColNum)
  }

  mounted() {
    this.initVisualizationPreviewStore()

    this.initEventBus()
    this.$nextTick(() => {
      //  get interactRelateList
      this.parseInteractInfo()

      this.gridList.forEach((gridItem: GridItemDataInterface) => {
        parseChartOptionToFunction(gridItem.chartOptions, gridItem.chartType)
        if (gridItem.chartType === 'table') {
          if (gridItem.chartOptions.dataId) {
            this.$set(gridItem.chartOptions, 'trigger', Math.random())
          }
        }
        if (gridItem.chartType === 'text') {
          this.$set(gridItem, 'isLoading', true)
        }
      })
    })
  }

  beforeDestroy() {
    this.destroyEventBus()
  }

  private initEventBus() {
    this.dataStore.eventBus.$on('graph-node-click', this.onGraphNodeClick)
  }

  private destroyEventBus() {
    this.dataStore.eventBus.$off('graph-node-click', this.onGraphNodeClick)
  }
  /** 解析gridList 各gridItem 中联动信息 */
  private parseInteractInfo() {
    // forEach this.gridList item - interactionJson - relateWidgets
    const result: any = []
    const reverseResult: any = []
    this.gridList.forEach((item: any) => {
      if (
        item.interactionJson?.relateWidgets &&
        item.interactionJson?.relateWidgets.length > 0
      ) {
        const list = item.interactionJson?.relateWidgets
        list.forEach((widget: any) => {
          if (widget.status === 'connected') {
            //  for reverse direction
            const indexR = reverseResult.findIndex(
              (i: any) => i.parent === item.widgetId
            )
            if (indexR >= 0) {
              /** 防止children是undfined */
              reverseResult[indexR].children =
                reverseResult[indexR].children || []
              reverseResult[indexR].children.push(widget.widgetId)
            } else {
              reverseResult.push({
                parent: item.widgetId,
                children: [widget.widgetId], // 后续需要push
              })
            }
            //  for right direction
            const index = result.findIndex(
              (i: any) => i.child === widget.widgetId
            )
            if (index >= 0) {
              result[index].parents.push(item.widgetId)
            } else {
              result.push({
                child: widget.widgetId,
                parents: [item.widgetId], // 后续需要push
              })
            }
          }
        })
      }
    })
    this.interactRelateList = result
    this.reverstInteractList = reverseResult

    //  get connected widget list; [1,2,3,4]
    this.dataStore.setHideWidgetList([])
    this.interactRelateList.forEach((item: any) => {
      this.dataStore.addHideWidget(item.child)
      this.dataStore.addParentWidgets(item.parents)
    })
  }

  public removeLoading(index: number) {
    this.$set(this.gridList[index], 'isLoading', false)
  }

  /**
   * 由于地图组件特殊,value 不是数组
   */
  checkIsNoData(gridItem: gridItemFilterInterface) {
    if (this.checkIsGeoMap(gridItem)) {
      return false
    }
    if (this.checkIsGraph(gridItem)) {
      return false
    }
    if (isArray(gridItem.chartOptions.value)) {
      return gridItem.chartOptions.value.length === 0
    }
    return false
  }

  sortLayoutItemsByRowCol(layout: Array<any>): Array<any> {
    return layout.sort((a, b) => {
      if (a.y > b.y || (a.y === b.y && a.x > b.x)) {
        return 1
      }
      return -1
    })
  }

  public get dataStore() {
    return VisualizationPreviewStore
  }

  public get isVisitor() {
    //  访客身份
    return UserStore.isProjectGuest
  }

  public created() {
    VisualizationStore.changeVisualizationMode('preview')
  }

  getTableData(
    gridItem: GridItemDataInterface,
    filterSend: Array<any>,
    withFilterSend?: boolean
  ) {
    const withFilter =
      this.dataStore.currentFilterFormAsCharts.includes(gridItem.widgetId) ||
      withFilterSend
    if (gridItem && gridItem.chartOptions && gridItem.chartType === 'table') {
      const filterFormJson = withFilter ? gridItem.filterFormJson || [] : []
      const filters = gridItem.chartOptions.filters || []
      const dataSend: KeyValueStore = {
        data: {
          id: gridItem.chartOptions.dataId,
          type: gridItem.chartOptions.dataType,
          tableJson: {
            curPage: withFilterSend ? 1 : gridItem.chartOptions.curPage,
            name: '_record_id_',
            pageSize: gridItem.chartOptions.pageSize,
            sortCol: gridItem.chartOptions.sortCol,
            filters: withFilter
              ? mergeFilterFormFilters(filters, filterFormJson)
              : filters,
          },
        },
      }
      this.$set(gridItem, 'isLoading', true)
      getWidgetData(dataSend)
        .then((response: any) => {
          if (response.data.code === 1106) {
            this.errorFilterWidgets.push(gridItem.i)
            this.errorMsgMap[gridItem.i] = response.data.message
          }
          if ([100, 1106].includes(response.data.code)) {
            const gridItemClone = cloneDeep(gridItem)
            const { chartOptions } = gridItemClone
            const index = this.getGridIndex(gridItem.i as string)
            this.$set(this.gridList, index, {
              ...gridItemClone,
              isLoading: false,
              chartOptions: {
                ...chartOptions,
                curPage: response.data.result?.page?.curPage || 0,
                totalElements: response.data.result?.page?.totalElements || 0,
                pageSize: response.data.result?.page?.pageSize || 50,
                value: response.data.result
                  ? parseGetDataResult(
                      response.data.result,
                      undefined,
                      gridItem
                    )
                  : [],
              },
            })
            //  remove target widget from hideList
            this.dataStore.removeHideWidget(gridItem.widgetId)
          }
          if (withFilter) {
            setTimeout(() => {
              this.releaseLoadingTag(gridItem.widgetId)
            }, 100)
          }
          this.$set(gridItem, 'isLoading', false)
        })
        .catch(() => {
          this.$set(gridItem, 'isLoading', false)
          if (withFilter) {
            setTimeout(() => {
              this.releaseLoadingTag(gridItem.widgetId)
            }, 100)
          }
        })
    } else {
      console.error('call error: getTableData must be used by table')
    }
  }

  /**
   * 获取筛选参数
   */
  getFilterFormData(dataId: string, componentId: string): KeyValueStore | null {
    const gridFilters = this.gridList.filter((gridItem) => {
      return gridItem.chartType === 'filter' && gridItem.dataId === dataId
    })
    if (gridFilters.length > 0) {
      const result: KeyValueStore = {}
      gridFilters.forEach((item) => {
        item.fieldConfig.forEach((fieldInfo: ControlConfigurationInterface) => {
          if (fieldInfo.associationChart.includes(componentId)) {
            result[fieldInfo.column.value] = {
              compType: fieldInfo.controlRender,
              columnType: fieldInfo.columnType,
              value: fieldInfo.value,
            }
          }
        })
      })
      return result
    }
    return null
  }

  checkIsGeoMap(item: KeyValueStore) {
    return (
      item.chartType &&
      isArray(item.chartType) &&
      item.chartType[1] === GEOMAP_NAME
    )
  }

  checkIsGraph(item: KeyValueStore) {
    return GRAPH_ARRAY.includes(
      Array.isArray(item.chartType) ? item.chartType[1] : item.chartType
    )
  }

  getFilterItem(
    fieldName: string,
    value: any,
    controlType: string,
    columnType?: string
  ): KeyValueStore | null {
    let result: any = null
    switch (controlType) {
      case componentTypeEnum.radio:
      case componentTypeEnum.select:
      case componentTypeEnum.remoteSelect:
        if (value && isString(value)) {
          result = {
            col: fieldName,
            filterType: '=',
            values: [],
            checkedList: [value],
          }
        }
        break

      case componentTypeEnum.checkbox:
        if (value && isArray(value) && value.length > 0) {
          result = {
            col: fieldName,
            type: columnType,
            filterType: 'in',
            values: [],
            checkedList: value,
          }
        }
        break

      case componentTypeEnum.rangeDay:
        if (isArray(value) && value.length === 2) {
          result = {
            col: fieldName,
            filterType: '[a,b]',
            type: 'timestamp',
            values: [value[0], value[1]],
            format: 'yyyy-MM-dd',
            checkedList: [value[0], value[1]],
          }
        }
        break
      case componentTypeEnum.rangeMonth:
        if (isArray(value) && value.length === 2) {
          result = {
            col: fieldName,
            filterType: '[a,b]',
            type: 'timestamp',
            values: [value[0], value[1]],
            format: 'yyyy-MM',
            checkedList: [value[0], value[1]],
          }
        }

        break
      case componentTypeEnum.rangeYear:
        if (isArray(value) && value.length === 2) {
          result = {
            col: fieldName,
            filterType: '[a,b]',
            type: 'timestamp',
            values: [value[0], value[1]],
            format: 'yyyy',
            checkedList: [value[0], value[1]],
          }
        }
        break
      default:
        break
    }

    return result
  }

  /** getData之后，绑定最新的data for gridItem */
  gridItemSyncBindData(parameterSet: any) {
    const { data, gridItem, index } = parameterSet
    const gridItemSet: GridItemDataInterface | null = bindChartData(
      gridItem,
      data
    )
    if (gridItemSet) {
      if (gridItemSet && gridItemSet.chartOptions) {
        const dom: any = (this.$refs.previewEl as HTMLElement).querySelector(
          // 注意这里是在 preview element 下面寻找 dom
          `#grid-item-${gridItemSet.i}`
        )
        if (dom) {
          gridItemSet.chartOptions.size = [dom.offsetWidth, dom.offsetHeight]
        }
      }
      setTimeout(() => {
        this.$set(gridItemSet, 'isLoading', false)
      }, 100)
      Vue.set(this.gridList, index, gridItemSet)
    }
  }

  //  remove selectedParentList;  remove selectedChartInfo
  private removeSelectedConfig(widgetId: Number) {
    const index = this.selecetedChartInfo.findIndex((i: any) => {
      return i.widgetId === widgetId
    })
    if (index >= 0) {
      this.dataStore.setSelectedChartInfo(
        this.selecetedChartInfo.slice(0, index)
      )
      // 【直接根据数组位置，移除后面的所有】
      this.selectedParentList = this.selectedParentList.slice(0, index)
    }
  }

  /** 用于存储code为1106的response的widget */
  public errorFilterWidgets = [] as Array<any>
  /** 错误消息 */
  public errorMsgMap = {} as any

  getWidgetData(
    dataSetting: KeyValueStore,
    gridItem: any,
    index: number,
    withFilter: boolean = false
  ) {
    if (checkIsFilterForm(gridItem)) {
      return
    }
    const filters: KeyValueStore | null = this.getFilterFormData(
      dataSetting.dataId,
      gridItem.i as string
    )
    const filterSend = [] as Array<any>
    if (filters) {
      Object.keys(filters).forEach((fieldName: string) => {
        const controlRender = filters[fieldName].compType
        const value = filters[fieldName].value as any
        if (
          !['rangeDay', 'rangeYear', 'rengeMonth'].includes(controlRender) ||
          (['rangeDay', 'rangeYear', 'rengeMonth'].includes(controlRender) &&
            value[0] &&
            value[1])
        ) {
          const filterItem = this.getFilterItem(
            fieldName,
            value,
            controlRender,
            filters[fieldName].columnType
          )
          if (filterItem) {
            filterSend.push(filterItem)
          }
        }
      })
    }
    const filterFormJson = gridItem.filterFormJson || []

    if (gridItem.chartType === 'table') {
      this.getTableData(gridItem, filterSend, withFilter)
      return
    }
    if (this.checkIsGraph(gridItem)) {
      this.getGraphData(gridItem, withFilter)
      return
    }
    const query = {
      data: {
        id: gridItem.chartOptions.dataId,
        type: gridItem.chartOptions.dataType,
        /** 将原先的参数拷贝一下，防止交互候对数据的污染 */
        widgetJson: cloneDeep(gridItem.dataQuery.data.widgetJson ?? {}),
      },
    }

    /** 筛选数据 */
    if (withFilter && query.data?.widgetJson?.config) {
      query.data.widgetJson.config.filters = mergeFilterFormFilters(
        query.data.widgetJson.config.filters || [],
        filterFormJson
      )
      //  remove selectedParentList;  remove selectedChartInfo
      this.removeSelectedConfig(gridItem.widgetId)
      this.addHideList(gridItem.widgetId)
    }

    const chartType = Array.isArray(gridItem.chartType)
      ? gridItem.chartType[1]
      : gridItem.chartType

    this.$set(gridItem, 'isLoading', true)
    this.errorFilterWidgets = this.errorFilterWidgets.filter(
      (item) => item !== gridItem.i
    )
    getWidgetData(query)
      .then((response) => {
        if (response.data.code === 1106) {
          this.errorFilterWidgets.push(gridItem.i)
          this.errorMsgMap[gridItem.i] = response.data.message
        }
        if ([100, 1106].includes(response.data.code)) {
          // 处理数据
          const dataResult =
            parseGetDataResult(
              response.data?.result || { columns: [], data: [], extraData: {} },
              chartType,
              gridItem
            ) || []
          // if (query.data.widgetJson !== undefined) {
          //   dataResult = restructGetDataResult(
          //     dataResult,
          //     query.data.widgetJson
          //   )
          // }

          this.gridItemSyncBindData({
            data: {
              query,
              data: dataResult,
              columns: gridItem.columns,
              formData: gridItem.chartOptions,
            },
            gridItem,
            index,
          })
          //  remove target widget from hideList
          this.dataStore.removeHideWidget(gridItem.widgetId)
        }
        if (withFilter) {
          this.releaseLoadingTag(gridItem.widgetId)
        }
        this.$set(gridItem, 'isLoading', false)
      })
      .catch(() => {
        this.$set(gridItem, 'isLoading', false)
        if (withFilter) {
          this.releaseLoadingTag(gridItem.widgetId)
        }
      })
  }

  /** 释放加载标识 */
  releaseLoadingTag(widgetId: string | number) {
    const key = `widget_${this.filterFormExecuteId}`
    this.$set(
      this.loadingMaps,
      key,
      this.loadingMaps[`widget_${this.filterFormExecuteId}`].filter(
        (id: any) => id !== widgetId
      )
    )
    setTimeout(() => {
      if (this.loadingMaps[key].length === 0) {
        this.isFilterFormTaskRunning = false
      }
    }, 100)
  }

  private get selecetedChartInfo() {
    return this.dataStore.selectedChartInfo
  }

  @Watch('selecetedChartInfo', { deep: true })
  private handleSelectedInfoChange(newValue: any) {
    if (newValue.length > 0) {
      this.checkIfEnableInteract()
    }
  }

  //  判断哪些被控制的chart, 是否满足条件getData
  private checkIfEnableInteract() {
    //  之前添加过的不用重复添加；！！！！！！
    const selectedWidgetLength = this.selectedParentList.length
    const latestSelectWidgetId = this.selecetedChartInfo[
      this.selecetedChartInfo.length - 1
    ].widgetId

    this.interactRelateList.forEach((item: any) => {
      /** 把筛选器组件先给屏蔽了 */
      if (checkIsFilterForm(item)) {
        return
      }
      //  item parents 均被选中
      if (checkArrayIncludes(this.selectedParentList, item.parents)) {
        // 包含关系
        const index: any = this.gridList.findIndex(
          (instance: any) => instance.widgetId === item.child
        )
        this.resetFiltersForItem(index, item.child)
        if (
          item.parents.length <= selectedWidgetLength &&
          item.child !== latestSelectWidgetId &&
          item.parents.includes(latestSelectWidgetId) &&
          index > -1
        ) {
          //  点击自己，不会重复渲染； 点击无关的chart， 已渲染的不要重复渲染
          // 达到渲染条件
          const gridItem = this.gridList[index]
          //  当前联动只支持table, chart
          if (gridItem.chartType === 'table') {
            this.getTableData(gridItem, [])
          } else {
            this.getWidgetData(gridItem.chartOptions, gridItem, index) //  widgetJson - config - filters 需要处理
          }
        }
      }
    })
  }

  /**
   * 为item  重置筛选配置
   * 设置相应的filter 为gridItem 【一对一控制； 多对一控制】【不考虑多级filter，只考虑一级】
   * */
  private resetFiltersForItem(index: number, targetId: number) {
    //  selecetedChartInfo 新增在末尾
    const targetItem = this.interactRelateList.find(
      (item) => item.child === targetId
    )
    if (checkIsFilterForm(targetItem) || index === -1) {
      return
    }

    const target = this.gridList[index]
    //  先置空，在对重新添加filter
    if (target.chartType === 'table' && target.chartOptions) {
      //  table 的情况
      this.gridList[index].chartOptions.filters = []
    } else if (this.gridList[index].dataQuery!.data.widgetJson.config) {
      //  非table 的情况
      this.gridList[index].dataQuery!.data.widgetJson.config.filters = []
    }

    this.selecetedChartInfo.forEach((item: any) => {
      if (targetItem.parents.includes(item.widgetId)) {
        // 只处理和自己相关的filter && 在移除filter 的时候，同样要移除 - child widget 要改变；
        if (target.chartType === 'table' && target.chartOptions) {
          //  table 的情况
          this.gridList[index].chartOptions.filters.push({
            col: item.xAttrStr,
            filterType: '=',
            values: [item.xAttrValue],
            desc: item.desc,
          })
        } else if (this.gridList[index].dataQuery!.data.widgetJson.config) {
          //  非table 的情况
          this.gridList[index].dataQuery!.data.widgetJson.config.filters.push({
            col: item.xAttrStr,
            filterType: '=',
            values: [item.xAttrValue],
            desc: item.desc,
          })
        }
      }
    })
  }

  /** CHECK 该图表区域是否被选中过，若已经选中 则返回true, 反之则false； */
  private checkIfChartSelected(item: any) {
    if (this.selecetedChartInfo.length === 0) {
      return false
    }
    const result = this.selecetedChartInfo.find((info: any) => {
      return item.widgetId === info.widgetId
    })
    if (result?.widgetId) {
      return (
        item.xAttrValue === result.xAttrValue &&
        item.xAttrStr === result.xAttrStr
      )
    }
    return false
  }
  /** graph 点击事件 */
  public onGraphNodeClick(data: any, item: any) {
    const isGraph = this.checkIsGraph(item)
    const { labelKey, interactColumnName } = item.chartOptions // interactColumnName 目前只在气泡图中用到，放开字段供选择

    //   check if item.widgetId is in
    if (this.dataStore.parentWidgetList.includes(item.widgetId)) {
      const result = {
        xAttrStr: !isGraph ? interactColumnName || labelKey : data.value.key,
        xAttrValue: !isGraph
          ? data.event.data.data[interactColumnName || labelKey]
          : data.value.value,
        // ...data.event.data.data,
        widgetId: item.widgetId,
        desc: !isGraph
          ? item.dataQuery.data.widgetJson.config.keys[0].desc
          : '',
      }
      if (isGraph && item.chartOptions.value.nodes.length > 0) {
        const targetValue = item.chartOptions.value.nodes[0].attrs.filter(
          (att: any) => att.key === data.value.key
        )[0].value
        result.desc = typeof targetValue === 'string' ? 'varchar' : 'int' // graph中的数据属性只会有字符串和数字两种类型
      }

      if (this.checkIfChartSelected(result)) {
        this.removeSelectedConfig(item.widgetId)
        this.addHideList(item.widgetId)
      } else {
        this.removeSelectedConfig(item.widgetId)
        this.addHideList(item.widgetId)
        //  同一个widget 只保留一个filter info
        this.dataStore.addSelectedChartInfo(result)

        this.selectedParentList = [
          ...new Set([...this.selectedParentList, item.widgetId]),
        ]
      }
    }
  }

  //  将目标widgetId 下关联的图表加入到hideList 中
  private addHideList(widgetId: Number) {
    //  add back hideWidgetList    reverstInteractList
    const targetChildren = this.reverstInteractList.find((x: any) => {
      return x.parent === widgetId
    })
    this.dataStore.addHideWidgets(targetChildren?.children || [])
  }

  /** 获取tooltip渲染文字 */
  public getGraphTooltipInfo(chart: any, setting: any): Array<string> {
    console.log('chart', chart, 'setting', setting)
    if (setting.tooltipType === 1 && setting.nodeTooltipInfo) {
      return isArray(setting.nodeTooltipInfo.attrs) &&
        setting.nodeTooltipInfo.attrs.length > 0
        ? setting.nodeTooltipInfo.attrs.reduce(
            (rec: Array<string>, item: any) => {
              rec.push(`${item.key}：${item.value}`)
              return rec
            },
            [] as Array<string>
          )
        : ['-- 空 -- ']
    }
    if (setting.tooltipType === 2 && setting.linkTooltipInfo) {
      return isArray(setting.linkTooltipInfo.attrs) &&
        setting.linkTooltipInfo.attrs.length > 0
        ? setting.linkTooltipInfo.attrs.reduce(
            (rec: Array<string>, item: any) => {
              rec.push(`${item.key}：${item.value}`)
              return rec
            },
            [] as Array<string>
          )
        : ['-- 空 -- ']
    }
    return ['-- 空 --']
  }

  /** 检测filter-form是否可执行或重置 */
  checkFilterFormIsEnabled(gridItem: GridItemDataInterface) {
    return checkFilterFormIsEnabled(gridItem)
  }

  /** 是否是筛选器控件 */
  checkIsFilterForm(gridItem: gridItemFilterInterface) {
    return checkIsFilterForm(gridItem)
  }

  /** 获取组件类型 */
  getChartType(gridItem: GridItemDataInterface) {
    if (checkIsFilterForm(gridItem)) {
      return filterFormTypeName
    }
    return Array.isArray(gridItem.chartType)
      ? gridItem.chartType[1]
      : gridItem.chartType
  }

  /** 重新请求数据 */
  reloadWidget(gridItem: GridItemDataInterface, index: number) {
    this.getWidgetData(gridItem.chartOptions, gridItem, index, true)
  }

  /** 获取gridIndex */
  getGridIndex(index: string) {
    return this.gridList.findIndex((item: any) => item.i === index)
  }

  hoverFilterFormInfo: HoverFilterFormInfo | null = null

  /** 筛选控件中的筛选条件改变 */
  filterChange(data: any, gridItem: GridItemDataInterface) {
    console.log('filterChange done', data, gridItem)
  }

  /** 获取关联图表 */
  get filterAssociationChart() {
    let result = [] as Array<string | number>
    if (this.hoverFilterFormInfo) {
      const currentGrid = this.gridList.find(
        (item) => item.i === this.hoverFilterFormInfo?.i
      )
      result = currentGrid?.chartOptions?.associationChart || []
    }
    return result
  }

  /** 鼠标移入filter-form */
  handleFilterFormMouseEnter(gridItem: GridItemDataInterface) {
    this.hoverFilterFormInfo = {
      i: gridItem.i as any,
      widgetId: gridItem.widgetId,
    }
  }

  /** 鼠标移出fitler-form */
  handleFilterFormMouseLeave(gridItem: GridItemDataInterface) {
    if (this.hoverFilterFormInfo?.i === gridItem.i) {
      this.hoverFilterFormInfo = null
    }
  }

  /** 筛选器运行状态检测对象 */
  loadingMaps: any = {}

  /** 判断筛选执行情况 */
  checkIsRunning(gridItem: GridItemDataInterface) {
    return (
      this.loadingMaps[`widget_${gridItem.widgetId}`] &&
      this.loadingMaps[`widget_${gridItem.widgetId}`].length > 0
    )
  }

  /** 加载关联图表数据 */
  loadAssociationCharts(gridItem: GridItemDataInterface) {
    const associationCharts = ((gridItem.chartOptions?.associationChart ||
      []) as Array<string | number>).filter((id) => {
      return this.gridList.findIndex((grid) => grid.widgetId === id) > -1
    })
    if (associationCharts.length === 0) {
      message.info('筛选器没有有效关联组件')
      this.filterFormExecuteId = ''
      return
    }
    this.filterFormExecuteId = gridItem.widgetId
    this.isFilterFormTaskRunning = true
    this.$set(
      this.loadingMaps,
      `widget_${gridItem.widgetId}`,
      cloneDeep(associationCharts)
    )
    const filterFormWidgetJSON = getFilterDataFromFilterForm(gridItem)
    associationCharts.forEach((wegetId) => {
      const asGridItem = this.gridList.find((item) => {
        return item.widgetId === wegetId
      })
      if (asGridItem) {
        this.$set(asGridItem, 'filterFormJson', filterFormWidgetJSON)
        this.getWidgetData(
          asGridItem.chartOptions,
          asGridItem,
          this.getGridIndex(asGridItem.i.toString()),
          true
        )
      }
    })
  }

  /** 重置filter-form */
  filterFormReset(gridItem: GridItemDataInterface) {
    const index = this.getGridIndex(gridItem.i as any)
    let queryData = {} as any
    const { chartOptions } = gridItem
    const form = this.$refs[`filter-form-${gridItem.i}`] || []

    if (form && (form as any)[0]) {
      queryData = (form as any)[0].getResetData() || {}
    }
    const gridItemSet = {
      ...gridItem,
      chartOptions: {
        ...chartOptions,
        queryData,
      },
    }

    this.$set(this.gridList, index, gridItemSet)
    this.$nextTick(() => {
      this.loadAssociationCharts(gridItemSet)
    })
  }

  /** 执行或者停止筛选 */
  filterFormExecute(isStart: boolean, gridItem: GridItemDataInterface) {
    if (isStart) {
      this.loadAssociationCharts(gridItem)
    } else {
      this.filterFormExecuteId = ''
    }
  }

  /**
   * 获取graph 数据
   */
  private async getGraphData(
    gridItem: GridItemDataInterface,
    withFilter?: boolean
  ) {
    this.$set(gridItem, 'isLoading', true)
    const callback = withFilter
      ? (data: any) => filterGraphData(gridItem.filterFormJson || [], data)
      : undefined
    const value = await getWidgetGraphData(gridItem, this.projectId, callback)
    if (gridItem.dataQuery?.data.widgetJson.config.filters.length > 0) {
      this.filterGraphData(
        value,
        gridItem.dataQuery?.data.widgetJson.config.filters
      )
    }
    const { nodePosition = [] } = gridItem.chartOptions // 处理position位置
    value.nodes.forEach((node: any) => {
      const targetPosition = (nodePosition as Array<{
        id: string
        x: number
        y: number
      }>).filter((p) => p.id === node.id)[0]
      if (targetPosition) {
        node.x = targetPosition.x
        node.y = targetPosition.y
      }
    })

    this.$set(gridItem.chartOptions, 'value', value)
    this.$set(gridItem, 'showChart', true)
    this.$set(gridItem, 'isLoading', false)

    this.dataStore.removeHideWidget(gridItem.widgetId)

    if (withFilter) {
      setTimeout(() => {
        this.releaseLoadingTag(gridItem.widgetId)
      }, 100)
    }
  }

  /**
   * 过滤graphData
   */
  private filterGraphData(
    value: any,
    filters: { col: string; values: any[] }[]
  ) {
    value.nodes = value.nodes.filter((node: any) => {
      // eslint-disable-next-line unicorn/prevent-abbreviations
      const parsedAttrs = node.attrs.reduce((accu: any, current: any) => {
        accu[current.key] = current.value
        return accu
      }, {})
      return filters.every((filter) =>
        filter.values.includes(parsedAttrs[filter.col])
      )
    })
  }

  get pipelineId() {
    return this.dataStore.pipelineId
  }

  get dataset(): any[] {
    return this.dataStore.dataset
  }
}
</script>

<style lang="less" scoped>
.preview {
  background-color: #ebeaef;
  height: 100vh;
  overflow: hidden;

  .content-box {
    background: var(--content-box-bg);
    height: calc(100vh - 64px);
    overflow-y: auto;
    width: 100%;
  }

  .content-box.report-mode {
    background: #f3f3f7;
  }

  .grid-item.report-mode {
    border: 1px solid rgba(233, 233, 233, 1);
  }

  .grid-item.disable-border {
    border: 1px solid transparent !important;

    &:hover {
      border: 1px solid transparent;
    }

    .grid-item-close {
      display: none;
    }
  }

  .grid-item {
    background-color: var(--component-bg-color);
    border: 1px solid var(--component-border-color);
    border-radius: 4px;
    overflow: hidden;

    &.active,
    &:hover {
      border: 1px solid #c5c9ff;

      /deep/ .comp-filter-form-toolbar {
        opacity: 1;
        pointer-events: all;
      }
    }
  }

  .grid-item.hover-border {
    border: 1px solid rgb(197, 201, 255);
  }

  .grid-item.select-border,
  .grid-item.active-border {
    border: 1px solid #6771ff;
  }

  .container {
    background-color: #fff;
    height: calc(100vh - 64px);
    margin: 0 auto;
  }

  .report-mode {
    .container {
      padding: 0 72px;
    }
  }

  .loading-mask,
  .no-data {
    align-content: center;
    align-items: center;
    display: flex;
    height: 100%;
    justify-content: center;
    text-align: center;
    width: 100%;
    z-index: 100;

    .loading-status-img,
    .no-data-img {
      display: block;
      height: auto;
      max-width: 75%;
      width: 208px;
    }
  }

  .text-editor {
    height: calc(100% - 60px);
    padding: 0 15px;
  }

  .height-reset {
    height: calc(100% - 18px);
  }
}

.interact-mask {
  background: var(--component-bg-color-oneop) !important;
  height: calc(100% - 38px);
  margin-top: 38px;
  position: absolute;
  width: 100%;
  z-index: 100;

  &.no-title {
    height: 100%;
    margin-top: 0;
  }
}

.loading-mask {
  background: rgba(0, 0, 0, 0.05) !important;
  position: absolute;
  z-index: 1;

  .loading-status {
    align-content: center;
    align-items: center;
    color: var(--title-color);
    display: flex;
    flex-flow: row nowrap;
    font-size: 12px;
    font-weight: 500;
    left: 50%;
    letter-spacing: 1px;
    line-height: 36px;
    position: absolute;
    top: 50%;
    transform: translate(-50%, -50%);
    vertical-align: middle;

    .loading-text {
      display: inline-block;
      padding-left: 4px;
    }

    .sync-icon {
      color: #5760e6;
      font-size: 24px;
    }
  }
}

.no-data {
  background: transparent;

  .no-data-block {
    align-items: center;
    display: flex;
    flex-flow: column;
    justify-content: center;
  }

  .no-data-text {
    color: #d9d9d9;
  }
}

.title-box {
  color: var(--title-color);
  font-size: 14px;
  font-weight: 600;
  height: 38px;
  letter-spacing: 1px;
  line-height: 38px;
  overflow: hidden;
  padding: 0 16px;
  text-overflow: ellipsis;
  white-space: nowrap;
  width: 100%;

  /deep/ .comp-filter-form-toolbar {
    position: absolute;
    right: 16px;
    top: 0;
    z-index: 10;
  }

  &.no-title {
    display: none;
    height: 24px;
  }
}

/deep/ .chart-title {
  font-size: 14px !important;
  font-weight: 600;
  height: 38px;
  letter-spacing: 1px;
  line-height: 38px;
  padding: 0 16px;
  text-align: left !important;
}

.graph-tooltip {
  background: #fff;
  border-radius: 2;
  box-shadow: 0 0 10px 0 rgba(0, 0, 0, 0.15);
  max-width: 200px;
  min-width: 80px;
  padding: 12px;
  pointer-events: none;
  position: absolute;
  z-index: 100000;

  p {
    color: #555;
    font-size: 12px;
    line-height: 16px;
    margin: 0;
    overflow: hidden;
    padding: 0;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
}

.filter-form-hander {
  background: transparent;
  box-sizing: border-box;
  height: 100%;
  left: 0;
  padding-top: 24px;
  position: absolute;
  top: 0;
  width: 100%;
  z-index: 8;
}

.preview .grid-item.remove-border {
  border: 0;
}

.remove-title {
  display: none;
}

.up-split {
  .blank {
    height: 54px;
    width: 100%;
  }

  .up-split-box {
    display: flex;
    height: 18px;
    justify-content: space-between;
  }
}

.down-split {
  .down-split-box {
    display: flex;
    height: 18px;
    justify-content: space-between;
  }

  .blank {
    height: 54px;
    width: 100%;
  }

  .gap {
    background: #f8f8f8;
    height: 20px;
    width: 100%;
  }
}

.download-btn {
  align-items: center;
  background: #fff;
  border-radius: 56px;
  bottom: 40px;
  box-shadow: 0 6px 16px 0 rgba(92, 104, 254, 0.05);
  cursor: pointer;
  display: flex;
  height: 56px;
  justify-content: center;
  position: fixed;
  right: 40px;
  width: 56px;

  .btn-icon {
    font-size: 26px;
  }
}
</style>
